package com.imooc.oa.service;

import com.imooc.oa.dao.EmployeeDao;
import com.imooc.oa.dao.LeaveFormDao;
import com.imooc.oa.dao.NoticeDao;
import com.imooc.oa.dao.ProcessFlowDao;
import com.imooc.oa.entity.Employee;
import com.imooc.oa.entity.LeaveForm;
import com.imooc.oa.entity.Notice;
import com.imooc.oa.entity.ProcessFlow;
import com.imooc.oa.service.exception.BussinessException;
import com.imooc.oa.utils.MybatisUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 请假单流程服务
 */
public class LeaveFormService {
    /**
     * 创建请假单
     * @param form 前端输入的请假单数据
     * @return 持久化后的请假单对象
     */
    public LeaveForm createLeaveForm(LeaveForm form){
        //获得保存到数据库后的LeaveForm对象
        LeaveForm savedForm = (LeaveForm) MybatisUtils.executeUpdate(sqlSession -> {
                //1.持久化form表单数据，8级以下员工表单状态为processing，8级(总经理)状态为approved
                //通过员工类的id获得指定员工对象，再获得员工等级进行判断
                EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
                Employee employee = employeeDao.selectById(form.getEmployeeId());
                if (employee.getLevel() == 8) {
                    form.setState("approved");
                } else {
                    form.setState("processing");
                }
                //将处理好的数据存储到数据库中
                LeaveFormDao leaveFormDao = sqlSession.getMapper(LeaveFormDao.class);
                leaveFormDao.insert(form);
                //2.增加第一条流程数据，说明表单已提交，状态为complete
                ProcessFlowDao processFlowDao=sqlSession.getMapper(ProcessFlowDao.class);
                ProcessFlow processFlow=new ProcessFlow();
                processFlow.setFormId(form.getFormId());
                processFlow.setOperatorId(employee.getEmployeeId());//这里是提交请假表，所以用员工编号
                processFlow.setAction("apply");//提交请假表
                processFlow.setCreateTime(new Date());
                processFlow.setOrderNo(1);//这是此请假表单流程的第一个节点
                processFlow.setState("complete");
                processFlow.setIsLast(0);//不是最后一个节点
                processFlowDao.insert(processFlow);
                //3.分情况创建其余流程数据
                //3.1 7级以下员工，生成部门经理审批任务，请假时间大于36小时，还需生成总经理审批任务
            //SimpleDateFormat的parse()方法返回的是一个java.util.Date类型对象，format()方法返回的是StringBuffer对象
            SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd-HH时");
            NoticeDao noticeDao=sqlSession.getMapper(NoticeDao.class);
                if(employee.getLevel()<7)
                {
                    //部门经理审批任务
                    ProcessFlow processFlow1=new ProcessFlow();
                    //获得上级主管
                    Employee employee1=employeeDao.selectLeader(employee);
                    processFlow1.setFormId(form.getFormId());
                    processFlow1.setOperatorId(employee1.getEmployeeId());//这里是提审批请假表，所以用上级主管编号
                    processFlow1.setAction("audit");//审批请假表
                    processFlow1.setCreateTime(new Date());
                    processFlow1.setOrderNo(2);//这是此请假表单流程的第2个节点
                    processFlow1.setState("process");//正在审批表单
                    //获得请假时间的总毫秒数
                    Long diff=form.getEndTime().getTime()-form.getStartTime().getTime();
                    Float hours=diff/(1000*3600*1f);//将毫秒数转换成小时数
                    //这里是审批流程所以得通过请假时间判断是否是最后一个节点
                    if(hours< BussinessConstants.MANAGER_AUDIT_HOURS) {
                        processFlow1.setIsLast(1);//最后一个节点
                        processFlowDao.insert(processFlow1);
                    }else{
                        processFlow1.setIsLast(0);//不是最后一个节点
                        processFlowDao.insert(processFlow1);
                        //总经理审批任务
                        //获得部门经理的上级主管总经理
                        Employee employee2=employeeDao.selectLeader(employee1);
                        ProcessFlow processFlow2=new ProcessFlow();
                        processFlow2.setFormId(form.getFormId());
                        processFlow2.setOperatorId(employee2.getEmployeeId());//这里是审批请假表，所以用上级主管编号
                        processFlow2.setAction("audit");//审批请假表
                        processFlow2.setCreateTime(new Date());
                        processFlow2.setOrderNo(3);//这是此请假表单流程的第3个节点
                        processFlow2.setState("ready");//因为部门经理正在审批表单，所以是ready
                        processFlow2.setIsLast(1);//最后一个节点
                        processFlowDao.insert(processFlow2);
                    }
                    //通知请假员工请假单已提交的消息，"[%s-%s]"表示"[开始时间-结束时间]"
                    String noticeContent=String.format("你的请假申请[%s-%s]已提交，请等待上级审批.",
                            simpleDateFormat.format(form.getStartTime()),
                            simpleDateFormat.format(form.getEndTime()) );
                    //将通知请假员工请假单已提交的消息持久化到数据库中
                    noticeDao.insert(new Notice(employee.getEmployeeId(), noticeContent));
                    //通知部门经理审批的消息
                    noticeContent = String.format("%s-%s提起请假申请[%s-%s]，请尽快审批",
                            employee.getTitle(), employee.getName(), simpleDateFormat.format(form.getStartTime()),
                            simpleDateFormat.format(form.getEndTime()));
                    //将通知部门经理审批的消息持久化到数据库中
                    noticeDao.insert(new Notice(employee1.getEmployeeId(),noticeContent));
                }
                //3.2 7级员工，生成总经理审批任务
                else if(employee.getLevel()==7)
                {
                    ProcessFlow processFlow3=new ProcessFlow();
                    Employee employee3=employeeDao.selectLeader(employee);
                    processFlow3.setFormId(form.getFormId());
                    processFlow3.setOperatorId(employee3.getEmployeeId());//这里是审批请假表，所以用上级主管编号
                    processFlow3.setAction("audit");//审批请假表
                    processFlow3.setCreateTime(new Date());
                    processFlow3.setOrderNo(2);//这是此请假表单流程的第2个节点
                    processFlow3.setState("process");
                    processFlow3.setIsLast(1);//最后一个节点
                    processFlowDao.insert(processFlow3);
                    //通知请假员工请假单已提交的消息，"[%s-%s]"表示"[开始时间-结束时间]"
                    String noticeContent=String.format("你的请假申请[%s-%s]已提交，请等待上级审批.",
                            simpleDateFormat.format(form.getStartTime()),
                            simpleDateFormat.format(form.getEndTime()) );
                    //将通知请假员工请假单已提交的消息持久化到数据库中
                    noticeDao.insert(new Notice(employee.getEmployeeId(), noticeContent));
                    //通知总经理审批的消息
                    noticeContent = String.format("%s-%s提起请假申请[%s-%s]，请尽快审批",
                            employee.getTitle(), employee.getName(), simpleDateFormat.format(form.getStartTime()),
                            simpleDateFormat.format(form.getEndTime()));
                    //将通知总经理审批的消息持久化到数据库中
                    noticeDao.insert(new Notice(employee3.getEmployeeId(),noticeContent));
                }
                //3.3 8级员工，生成总经理审批任务，系统自动通过
                else if(employee.getLevel()==8)
                {
                    ProcessFlow processFlow4=new ProcessFlow();
                    processFlow4.setFormId(form.getFormId());
                    processFlow4.setOperatorId(employee.getEmployeeId());//这里是审批请假表，所以用上级主管编号
                    processFlow4.setAction("audit");//审批请假表
                    processFlow4.setCreateTime(new Date());
                    processFlow4.setIsLast(1);//是最后一个节点
                    processFlow4.setOrderNo(2);//这是此请假表单流程的第2个节点
                    //因为是8级员工，所以会自动审批通过，审批状态就是complete，审批时间就是创建任务流程的时间
                    processFlow4.setResult("approved");
                    processFlow4.setReason("自动通过");
                    processFlow4.setAuditTime(new Date());
                    processFlow4.setState("complete");
                    processFlowDao.insert(processFlow4);
                    //通知总经理请假单已自动提交并通过的消息，"[%s-%s]"表示"[开始时间-结束时间]"
                    String noticeContent=String.format("你的请假申请[%s-%s]系统已自动批准通过已提交.",
                            simpleDateFormat.format(form.getStartTime()),
                            simpleDateFormat.format(form.getEndTime()) );
                    //将通知总经理请假单已提交的消息持久化到数据库中
                    noticeDao.insert(new Notice(employee.getEmployeeId(), noticeContent));
                    //总经理自动提交通过不需要上级主管审批了
                }
                return  form;
            });
        return savedForm;
    }
    /**
     * 获取指定任务状态及指定经办人对应的请假单列表
     * @param pfState ProcessFlow任务状态
     * @param pfOperatorId 经办人编号
     * @return 请假单及相关数据列表
     */
    public List<Map> getLeaveFormList(String pfState,Long pfOperatorId)
    {
            return (List<Map>) MybatisUtils.executeQuery(sqlSession ->
            {
                LeaveFormDao leaveFormDao=sqlSession.getMapper(LeaveFormDao.class);
                List<Map> formList=leaveFormDao.selectByParams(pfState, pfOperatorId);
                return  formList;
            });

    }
    /**
     * 审核请假单
     * @param formId 表单编号
     * @param operatorId 经办人(当前登录员工)
     * @param result 审批结果
     * @param reason 审批意见
     */
    public void audit(Long formId,Long operatorId,String result,String reason)
    {

        MybatisUtils.executeUpdate(sqlSession -> {
            //1.无论同意或者驳回当前任务状态变更为complete
           ProcessFlowDao processFlowDao=sqlSession.getMapper(ProcessFlowDao.class);
           List<ProcessFlow> flowList=processFlowDao.selectByFormId(formId);
           if(flowList.size()==0)
           {
               throw new BussinessException("PF001", "无效的审批流程");
           }
           //通过流式编程获取当前任务ProcessFlow对象，即获得当前用户未进行审批的审批任务
           List<ProcessFlow> processList=flowList.stream().filter(p->p.getOperatorId()==operatorId
                   &&p.getState().equals("process")).collect(Collectors.toList());
            ProcessFlow processFlow=null;
           if(processList.size()==0)
           {
               throw new BussinessException("PF002", "未找到待处理任务");
           }
           else{
               processFlow=processList.get(0);
               //对待处理任务数据进行更新
               processFlow.setState("complete");
               processFlow.setResult(result);
               processFlow.setReason(reason);
               processFlow.setAuditTime(new Date());
               processFlowDao.update(processFlow);
           }
           //2.如果当前任务是最后一个节点，代表流程结束，更新LeaveForm请假单状态为对应的approved/refused
            LeaveFormDao leaveFormDao=sqlSession.getMapper(LeaveFormDao.class);
            LeaveForm leaveForm=leaveFormDao.selectById(formId);//获得当前审批的请假单对象
            SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd-HH时");
            EmployeeDao employeeDao=sqlSession.getMapper(EmployeeDao.class);
            Employee employee = employeeDao.selectById(leaveForm.getEmployeeId());//提交请假单的员工
            Employee operator=employeeDao.selectById(operatorId);//获得任务经办人信息
            NoticeDao noticeDao=sqlSession.getMapper(NoticeDao.class);
            if(processFlow.getIsLast()==1)
            {

                leaveForm.setState(result);
                leaveFormDao.update(leaveForm);//对数据库中的请假表进行更新
                String strResult=null;
                if(result.equals("approved"))
                {
                    strResult="批准";
                }
                else if(result.equals("refused"))
                {
                    strResult = "驳回";
                }
                //发给表单提交人的通知
                String noticeContent=String.format("你的请假申请[%s-%s]%s%s已%s，审批意见：%s，审批流程已结束",
                        dateFormat.format(leaveForm.getStartTime()),
                        dateFormat.format(leaveForm.getEndTime()),
                        operator.getTitle(),operator.getName(),strResult,reason
                        );
                noticeDao.insert(new Notice(leaveForm.getEmployeeId(),noticeContent));
                //发给进行审批的主管的通知
                noticeContent = String.format("%s-%s提起请假申请[%s-%s]您已%s,审批意见:%s,审批流程已结束" ,
                        employee.getTitle() , employee.getName() ,
                        dateFormat.format( leaveForm.getStartTime()) ,
                        dateFormat.format(leaveForm.getEndTime()),
                        strResult , reason);//发给审批人的通知
                //将通知持久化
                noticeDao.insert(new Notice(operator.getEmployeeId(),noticeContent));

            }
            else
            {
                    //readyList包含所有的后续节点
                    List<ProcessFlow> readyList=flowList.stream().filter(p->p.getState().equals("ready"))
                            .collect(Collectors.toList());
                    //3.如果当前任务不是最后一个节点且审批通过，那么下一个节点的状态从ready变为process
                if(result.equals("approved"))
                {
                    ProcessFlow nextProcessFlow=readyList.get(0);
                    nextProcessFlow.setState("process");
                    //对数据库中下一个节点进行更新操作
                    processFlowDao.update(nextProcessFlow);
                    //消息1: 通知表单提交人,部门经理已经审批通过,交由上级继续审批
                    String noticeContent1 = String.format("您的请假申请[%s-%s]%s%s已批准,审批意见:%s ,请继续等待上级审批" ,
                            dateFormat.format(leaveForm.getStartTime()) , dateFormat.format(leaveForm.getEndTime()),
                            operator.getTitle() , operator.getName(),reason);
                    noticeDao.insert(new Notice(leaveForm.getEmployeeId(),noticeContent1));

                    //消息2: 通知总经理有新的审批任务
                    String noticeContent2 = String.format("%s-%s提起请假申请[%s-%s],请尽快审批" ,
                            employee.getTitle() , employee.getName() , dateFormat.format(leaveForm.getStartTime()) ,
                            dateFormat.format(leaveForm.getEndTime()));
                    noticeDao.insert(new Notice(nextProcessFlow.getOperatorId(),noticeContent2));

                    //消息3: 通知部门经理(当前经办人),员工的申请单你已批准,交由上级继续审批
                    String noticeContent3 = String.format("%s-%s提起请假申请[%s-%s]您已批准,审批意见:%s,申请转至上级领导继续审批" ,
                            employee.getTitle() , employee.getName() , dateFormat.format( leaveForm.getStartTime()) ,
                            dateFormat.format(leaveForm.getEndTime()), reason);
                    noticeDao.insert(new Notice(operator.getEmployeeId(), noticeContent3));

                }
                else if(result.equals("refused")){
                //4。如果当前任务不是最后一个节点且审批驳回，则后续所有任务状态变为cancel，请假单状态变为refused
                    for(ProcessFlow p:readyList)
                    {
                        p.setState("cancel");
                        processFlowDao.update(p);//别忘了对数据库进行更新
                    }
                    leaveForm.setState("refused");
                    leaveFormDao.update(leaveForm);
                    //消息1: 通知申请人表单已被驳回
                    String noticeContent1 = String.format("您的请假申请[%s-%s]%s%s已驳回,审批意见:%s,审批流程已结束" ,
                            dateFormat.format(leaveForm.getStartTime()) ,
                            dateFormat.format(leaveForm.getEndTime()),
                            operator.getTitle() , operator.getName(),reason);
                    noticeDao.insert(new Notice(leaveForm.getEmployeeId(),noticeContent1));

                    //消息2: 通知经办人表单"您已驳回"
                    String noticeContent2 = String.format("%s-%s提起请假申请[%s-%s]您已驳回,审批意见:%s,审批流程已结束" ,
                            employee.getTitle() , employee.getName() ,
                            dateFormat.format( leaveForm.getStartTime()) ,
                            dateFormat.format(leaveForm.getEndTime()), reason);
                    noticeDao.insert(new Notice(operator.getEmployeeId(),noticeContent2));

                }
            }
            return null;
        });

    }

    }
